/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package asis;

import asis.ini.INI;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import skyproc.*;
import skyproc.exceptions.NotFound;
import skyproc.genenums.Skill;
import skyproc.gui.SPProgressBarPlug;

/**
 *
 * @author pc tech
 */
public class AutomaticSpells {

    public void addSpells() throws NotFound, IOException {
        internalAddSpells();
    }

    public void addSpells(ASIS a) {
        if (a.getModExclusions() != null) {
            NPCModExclusions.addAll(a.getModExclusions());
        }
        internalAddSpells();
    }

    private void internalAddSpells() {
        SPProgressBarPlug.setStatus("Automatic Spells: Starting Patch");
        SPProgressBarPlug.incrementBar();
        Mod merger = new Mod("TmpMerger", false);
        merger.addAsOverrides(SPGlobal.getDB());

        processINI();
        getBlockList();
        getSkillIndices();
        getPerkLists(merger);
        getSpellSettings();
        getSpellLists(merger);
        addSpellsToNPCs(merger);
    }

    private void addSpellsToNPCs(Mod merger) {
        SPProgressBarPlug.setStatus("Automatic Spells: Adding Spells to NPCs");
        SPProgressBarPlug.incrementBar();

        boolean npcInclude;
        boolean spellInclude;
        
//        JOptionPane.showMessageDialog(null, "Number of Spells:\n" + spells.size());

        for (NPC_ n : merger.getNPCs()) {

            npcInclude = getValidNPC(n);
            if (npcInclude) {
                for (Spell s : spells) {
                    spellInclude = getValidSpell(s);
//                    if (spellInclude) {
//                        JOptionPane.showMessageDialog(null, "Spell Include!\n" + n.getEDID());
//                    }
                    if (n.getEDID().toUpperCase().contains("DRAUGR") && !s.getEDID().toUpperCase().contains("LEFTHAND")) {
                        spellInclude = false;
                    }
                    if (spellInclude) {
                        if (n.get(s.getSpellType()) > s.getSpellLevel()) {
                            n.addSpell(s.getSpell().getForm());
                            SPGlobal.getGlobalPatch().addRecord(n);
                        }
                    }
                }
            }
        }
    }

    private void getSpellLevel(Perk p, SPEL s) {

        int temp;

        for (int i = 0; i < p.getNumConditionals(); i++) {
            temp = p.getPerkType(i);
            if (temp != -1) {
                spellType = perkSettingsMap.get(temp);
            }
        }
        spellLevel = (int) p.getPerkLevel(0);

        if (spellType == Skill.ALTERATION) {
            if (!s.getEDID().toUpperCase().contains("LEFTHAND") && spellLevel != skillLevels[4]) {
                spellLevel += 25;
            } else if (s.getEDID().toUpperCase().contains("RIGHTHAND")) {
                spellLevel += 100;
            }
        } else if (spellType == Skill.CONJURATION) {
            if (s.getEDID().toUpperCase().contains("LEFTHAND")) {
                spellLevel += 100;
            } else if (!s.getEDID().toUpperCase().contains("RIGHTHAND") && spellLevel != skillLevels[4]) {
                spellLevel += 25;
            }
        } else if (spellType == Skill.DESTRUCTION) {
            if (s.getEDID().toUpperCase().contains("LEFTHAND")) {
                spellLevel += 100;
            } else if (!s.getEDID().toUpperCase().contains("RIGHTHAND") && spellLevel != skillLevels[4]) {
                spellLevel += 25;
            }
        } else if (spellType == Skill.RESTORATION) {
            if (!s.getEDID().toUpperCase().contains("LEFTHAND") && spellLevel != skillLevels[4]) {
                spellLevel += 25;
            } else if (s.getEDID().toUpperCase().contains("RIGHTHAND")) {
                spellLevel += 100;
            }
        } else if (spellType == Skill.ILLUSION) {
            if (!s.getEDID().toUpperCase().contains("LEFTHAND") && spellLevel != skillLevels[4]) {
                spellLevel += 25;
            } else if (s.getEDID().toUpperCase().contains("RIGHTHAND")) {
                spellLevel += 100;
            }
        } else {
            spellLevel = -1;
        }
    }

    private void getSpellLists(Mod merger) {
        SPProgressBarPlug.setStatus("Automatic Spells: Mapping Spells");
        SPProgressBarPlug.incrementBar();
        FormID perkRef;
        Perk perk;
//        JOptionPane.showMessageDialog(null, "Perk Size:\n" + perks.size());
        for (SPEL s : merger.getSpells()) {
            if (s.getEDID().toUpperCase().contains("STORMCALLLIGHTNINGBOLT")) {
                int abc = 0;
            }
            perkRef = s.getPerkRef();
//            for (FormID pr : perks.keySet()) {
//                Perk testPerk = perks.get(pr);
//                JOptionPane.showMessageDialog(null, "Perk from Map:\n" + testPerk.getEDID());
//            }
//            JOptionPane.showMessageDialog(null, "Perk:\n" + perkRef.getTitle());
            if (perks.containsKey(perkRef)) {
                perk = perks.get(perkRef);
                getSpellLevel(perk, s);
                if (spellLevel != -1 && spellType != null) {
                    Spell spell = new Spell(s, spellLevel, spellType);
                    spells.add(spell);
                }
            }
        }
    }

    private void getPerkLists(Mod merger) {
        SPProgressBarPlug.setStatus("Automatic Spells: Mapping Perks");
        SPProgressBarPlug.incrementBar();
        Perk tempPerk = null;
        FormID nextPerkForm;
        int numConditions;
        boolean perkUsed;
        for (PERK p : merger.getPerks()) {
            perkUsed = false;
            numConditions = 0;
            for (Condition c : p.getConditions()) {

                
//                if (p.getEDID().toUpperCase().contains("DUAL")) JOptionPane.showMessageDialog(null, "Perk:\n" + p.getEDID() + "\nReference:\n" + c.getReference() + "\nString:\n" + c.toString() + "\nFunction:\n" + c.getFunction() + "\nParam1:\n" + c.getParam1() + "\nParam2:\n" + c.getParam2() + "\nParam3:\n" + c.getParam3());
//                JOptionPane.showMessageDialog(null, "Reference:\n" + c.getReference());
//                JOptionPane.showMessageDialog(null, "String:\n" + c.toString());
//                JOptionPane.showMessageDialog(null, "Function:\n" + c.getFunction());
//                JOptionPane.showMessageDialog(null, "Major Record:\n" + SPDatabase.getMajor(c.getReference(), GRUP_TYPE.AVIF));
                
                if (c.getOperator() == Condition.Operator.GreaterThanOrEqual && c.getFunction() == Condition.P_Int.GetBaseActorValue && !perkUsed) {
                    if (p.getNextPerk() != null) {
                        nextPerkForm = p.getNextPerk();
                    } else {
                        nextPerkForm = p.getForm();
                    }
                    
         //           int avIndex = 0;
         //           String testString = c.getParam1().toString();
         //           avIndex = Integer.parseInt(testString.substring(0,6),16);
//                        JOptionPane.showMessageDialog(null, "There has been an error with Perk Mapping. Perk:\n" + p.getEDID() + "\nString:\n" + c.toString() + "\nFunction:\n" + c.getFunction() + "\nParam1:\n" + c.getParam1() + "\nDescription:\n" + p.getDescription());
         //           String avFormNum = Integer.toHexString(avIndex + (0x446));
//                    FormID testForm = new FormID("000452", SPGlobal.getDB().getMod(new ModListing("Skyrim", true)).getInfo());
//                    MajorRecord testMajor = SPDatabase.getMajor(testForm);
//                    JOptionPane.showMessageDialog(null, testMajor);
//                    JOptionPane.showMessageDialog(null, "Perk:\n" + p.getEDID());
//                    JOptionPane.showMessageDialog(null, "Perk:\n" + p.getEDID() + "\navFormNum:\n" + avFormNum + "\nString:\n" + c.toString() + "\nFunction:\n" + c.getFunction() + "\nParam1FormStr:\n" + ((FormID)c.getParam1()).getFormStr()+ "\nMajor:\n" + SPDatabase.getMajor(new FormID(avFormNum, SPGlobal.getDB().getMod(new ModListing("Skyrim", true)).getInfo())));
         //           String avMasterStr = testString.substring(6,testString.length() - 4);
         //           boolean avMaster = testString.charAt(testString.length()-1) == 'm';
         //           String av = ((AVIF) SPDatabase.getMajor(new FormID(avFormNum, SPGlobal.getDB().getMod(new ModListing(avMasterStr, avMaster)).getInfo()))).getEDID();

                    Integer val = (Integer) c.getParam1();
                    String av = skyproc.genenums.ActorValue.values()[val.intValue()].name();
                    tempPerk = new Perk(p, getSkillInt(av.toUpperCase()), c.getValueFloat(), nextPerkForm);
                    perks.put(tempPerk.getPerkForm(), tempPerk);
                    numConditions++;

                } else if (c.getOperator() == Condition.Operator.GreaterThanOrEqual && c.getFunction() == Condition.P_Int.GetBaseActorValue && perkUsed) {
                    String av = ((AVIF) SPDatabase.getMajor(c.getReference(), GRUP_TYPE.AVIF)).getEDID();
                    tempPerk.addConditional(getSkillInt(av.toUpperCase()), c.getValueFloat());
                }
            }
        }
    }

    private boolean getValidNPC(NPC_ n) {
        boolean npcExclude = false;
        boolean npcInclude = false;
        String list;
        String edid = n.getEDID().toUpperCase();
        if (!npcBlockList.contains(n.getForm())) {
            if (npcInclusions.isEmpty()) {
                npcInclude = true;
            }
            if (!npcInclude) {
                for (int i = 0; i < npcInclusions.size(); i++) {
                    list = (String) npcInclusions.get(i);
                    if (edid.contains(list)) {
                        npcInclude = true;
                        break;
                    }
                }
            }
            if (npcExclusions.isEmpty() && NPCModExclusions.isEmpty()) {
                npcExclude = false;
            }
            if (npcInclude && !npcExclude) {
                for (int i = 0; i < npcExclusions.size(); i++) {
                    list = ((String) npcExclusions.get(i));
                    if (edid.contains(list)) {
                        npcExclude = true;
                        break;
                    }
                }
            }

            if (npcInclude && !npcExclude) {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

    private boolean getValidSpell(Spell s) {
        boolean spellExclude = false;
        boolean spellInclude = false;
        String list;
        String edid = s.getEDID().toUpperCase();

        if (spellInclusions.isEmpty() && spellModInclusions.isEmpty()) {
            spellInclude = true;
        }
        if (!spellInclude) {
            for (int i = 0; i < spellInclusions.size(); i++) {
                list = ((String) spellInclusions.get(i));
                if (edid.contains(list)) {
                    spellInclude = true;
                    break;
                }
            }
        }
        if (!spellInclude) {
            for (int i = 0; i < spellModInclusions.size(); i++) {
                list = (String) spellModInclusions.get(i);
                if (s.getModMaster().equals(new ModListing(list))) {
                    spellInclude = true;
                    break;
                }
            }
        }

        if (spellExclusions.isEmpty() && spellExclusionsStartsWith.isEmpty()) {
            spellExclude = false;
        } else if (spellInclude) {
            for (int i = 0; i < spellExclusionsStartsWith.size(); i++) {
                list = ((String) spellExclusionsStartsWith.get(i));
                if (edid.startsWith(list)) {
                    spellExclude = true;
                    break;
                }
            }

            if (!spellExclude) {
                for (int i = 0; i < spellExclusions.size(); i++) {
                    list = ((String) spellExclusions.get(i));
                    if (edid.contains(list)) {
                        spellExclude = true;
                        break;
                    }
                }
            }
        }
        if (spellInclude && !spellExclude) {
            return true;
        } else {
            return false;
        }
    }

    private void getSpellSettings() {
        SPProgressBarPlug.setStatus("Automatic Spells: Generating Spell Settings");
        SPProgressBarPlug.incrementBar();
        String list;
        for (Map.Entry<String, String> entry : spellSettingsMap.entrySet()) {
            list = entry.getKey();
            switch (list.toUpperCase()) {
                case "NOVICELEVEL":
                    skillLevels[0] = Integer.parseInt(spellSettingsMap.get(list));
                    break;
                case "APPRENTICELEVEL":
                    skillLevels[1] = Integer.parseInt(spellSettingsMap.get(list));
                    break;
                case "ADEPTLEVEL":
                    skillLevels[2] = Integer.parseInt(spellSettingsMap.get(list));
                    break;
                case "EXPERTLEVEL":
                    skillLevels[3] = Integer.parseInt(spellSettingsMap.get(list));
                    break;
                case "MASTERLEVEL":
                    skillLevels[4] = Integer.parseInt(spellSettingsMap.get(list));
                    break;
            }
        }
    }

    private void getSkillIndices() {
        int i = 6;
        for (Skill s : Skill.values()) {
            perkSettingsMap.put(i, s);
            skillAVMap.put(s.toString().toUpperCase(), i);
            i++;
        }
    }

    private void processINI() {
        SPProgressBarPlug.setStatus("Automatic Spells: Processing INI");
        SPProgressBarPlug.incrementBar();
        //Sets up the file reader for the ini file.
        INI ini = null;

        //Sets up the file reader for the ini file.
        try {
            ini = new INI("AutomaticSpells.ini");
        } catch (IOException ex) {
            Logger.getLogger(NPCPotions.class.getName()).log(Level.SEVERE, null, ex);
        }

        Collection<INI.IniSectionHead> sections = getSectionList();

        ini.addSection(sections);

        try {
            ini.readData();
        } catch (IOException ex) {
            Logger.getLogger(NPCPotions.class.getName()).log(Level.SEVERE, null, ex);
        }

        initializeLists(ini);
    }

    private void getBlockList() {
        //Clean-up NPC's from excluded mods.  This is done as blocking the import 
        // could cause previous NPC's to override an excluded mods NPC's.
        String list;
        Mod tempMod;
        for (int i = 0; i < NPCModExclusions.size(); i++) {
            list = (String) NPCModExclusions.get(i);
            try {
                ModListing huh = new ModListing(list);
                SPDatabase whut = SPGlobal.getDB();
                tempMod = whut.getMod(huh);
                for (NPC_ n : tempMod.getNPCs()) {
                    npcBlockList.add(n.getForm());
                }
            } catch (Exception e) {
                //Do nothing, probably doesn't have the mod in the exclusions.
            }
        }
    }

    private Collection<INI.IniSectionHead> getSectionList() {
        Collection<INI.IniSectionHead> sectionList = new ArrayList<>();

        for (IniSection currentSection : IniSection.values()) {
            sectionList.add(new INI.IniSectionHead(currentSection.getName(), currentSection.getFormat()));
        }

        return sectionList;
    }

    private void initializeLists(INI ini) {
        npcInclusions = (ArrayList) ini.getCollection(new INI.IniSectionHead(IniSection.NPCINCLUSIONS.getName(),
                IniSection.NPCINCLUSIONS.getFormat()));
        for (int i = 0; i < npcInclusions.size(); i++) {
            npcInclusions.set(i, ((String) npcInclusions.get(i)).toUpperCase());
        }
        npcExclusions = (ArrayList) ini.getCollection(new INI.IniSectionHead(IniSection.NPCEXCLUSIONS.getName(),
                IniSection.NPCEXCLUSIONS.getFormat()));
        for (int i = 0; i < npcExclusions.size(); i++) {
            npcExclusions.set(i, ((String) npcExclusions.get(i)).toUpperCase());
        }
        NPCModExclusions.addAll((ArrayList) ini.getCollection(new INI.IniSectionHead(IniSection.NPCMODEXCLUSIONS.getName(),
                IniSection.NPCMODEXCLUSIONS.getFormat())));

        spellSettingsMap = ini.getMap(new INI.IniSectionHead(IniSection.SPELLSETTINGS.getName(),
                IniSection.SPELLSETTINGS.getFormat()));

        spellExclusions = (ArrayList) ini.getCollection(new INI.IniSectionHead(IniSection.SPELLEXCLUSIONSCONTAINS.getName(),
                IniSection.SPELLEXCLUSIONSCONTAINS.getFormat()));
        for (int i = 0; i < spellExclusions.size(); i++) {
            spellExclusions.set(i, ((String) spellExclusions.get(i)).toUpperCase());
        }
        spellExclusionsStartsWith = (ArrayList) ini.getCollection(new INI.IniSectionHead(IniSection.SPELLEXCLUSIONSSTARTSWITH.getName(),
                IniSection.SPELLEXCLUSIONSSTARTSWITH.getFormat()));
        for (int i = 0; i < spellExclusionsStartsWith.size(); i++) {
            spellExclusionsStartsWith.set(i, ((String) spellExclusionsStartsWith.get(i)).toUpperCase());
        }
        spellModInclusions = (ArrayList) ini.getCollection(new INI.IniSectionHead(IniSection.SPELLMODINCLUSIONS.getName(),
                IniSection.SPELLMODINCLUSIONS.getFormat()));
        for (int i = 0; i < spellModInclusions.size(); i++) {
            spellModInclusions.set(i, ((String) spellModInclusions.get(i)).toUpperCase());
        }
        spellInclusions = (ArrayList) ini.getCollection(new INI.IniSectionHead(IniSection.SPELLINCLUSIONS.getName(),
                IniSection.SPELLINCLUSIONS.getFormat()));
        for (int i = 0; i < spellInclusions.size(); i++) {
            spellInclusions.set(i, ((String) spellInclusions.get(i)).toUpperCase());
        }
    }

    private enum IniSection {

        SPELLSETTINGS(INI.IniDataFormat.KEY_VALUE, "SPELLSETTINGS"),
        NPCINCLUSIONS(INI.IniDataFormat.VALUE, "NPCINCLUSIONS"),
        NPCEXCLUSIONS(INI.IniDataFormat.VALUE, "NPCEXCLUSIONS"),
        NPCMODEXCLUSIONS(INI.IniDataFormat.VALUE, "NPCMODEXCLUSIONS"),
        SPELLEXCLUSIONSCONTAINS(INI.IniDataFormat.VALUE, "SPELLEXCLUSIONSCONTAINS"),
        SPELLEXCLUSIONSSTARTSWITH(INI.IniDataFormat.VALUE, "SPELLEXCLUSIONSSTARTSWITH"),
        SPELLMODINCLUSIONS(INI.IniDataFormat.VALUE, "SPELLMODINCLUSIONS"),
        SPELLINCLUSIONS(INI.IniDataFormat.VALUE, "SPELLINCLUSIONS");
        private INI.IniDataFormat format;
        private String name;

        IniSection(INI.IniDataFormat format, String name) {
            this.format = format;
            this.name = name;
        }

        String getName() {
            return name;
        }

        INI.IniDataFormat getFormat() {
            return format;
        }
    }
    
    int getSkillInt(String av) {
        if (skillAVMap.containsKey(av)) {
            return skillAVMap.get(av);
        }
        return 0;
    }
    
    String settingType = null;
    String settingValue = null;
    int spellLevel;
    Skill spellType;
    int[] skillLevels = {0, 0, 0, 0, 0};
    static Map<String, Integer> skillAVMap = new HashMap<>();
    ArrayList<String> npcInclusions = new ArrayList<>(0);
    ArrayList<String> npcExclusions = new ArrayList<>(0);
    ArrayList<String> NPCModExclusions = new ArrayList<>(0);
    ArrayList<String> spellInclusions = new ArrayList<>(0);
    ArrayList<String> spellExclusions = new ArrayList<>(0);
    ArrayList<String> spellExclusionsStartsWith = new ArrayList<>(0);
    ArrayList<String> spellModInclusions = new ArrayList<>(0);
    ArrayList<FormID> npcBlockList = new ArrayList<>();
    ArrayList<FormID> spellBlockList = new ArrayList<>();
    ArrayList<Spell> spells = new ArrayList<>();
    Map<FormID, Perk> perks = new HashMap<>();
    Map<String, String> spellSettingsMap = new HashMap<>();
    Map<Integer, Skill> perkSettingsMap = new LinkedHashMap<>();
    public int numProgressSections = 5;
}